#include "stdafx.h"

namespace classifier_test
{

bool cont_scheme::valid(double Val) const
{
	return Val >= lower_bound && Val <= upper_bound;
}

cont_domain cont_scheme::to_domain() const
{
	return cont_domain(lower_bound, upper_bound);
}

void cont_scheme::format(file_formatter& Fmt)
{
	Fmt >> lower_bound << ',' >> upper_bound;
}

void cont_scheme::serialize(serializer& Serializer)
{
	Serializer("lower_bound", lower_bound)("upper_bound", upper_bound);
}

discr_scheme::discr_scheme()
	: count(Count), Count(0)
{
}

discr_scheme::discr_scheme(const discr_scheme& Val)
	: count(Count), Count(0)
{
	*this = Val;
}

discr_scheme& discr_scheme::operator=(const discr_scheme& Val)
{
	if (&Val == this) return *this;
	clear();
	Count = Val.Count;
	Values.resize(Count);
	for (int I = 0; I < Count; I++)
		Values[I] = Map.insert(make_pair(Val.Values[I]->first, I));
	return *this;
}

void discr_scheme::clear()
{
	Count = 0;
	reset_vector(Values);
	Map.clear();
}

int discr_scheme::get_count() const
{
	return Count;
}

void discr_scheme::set_count(int Val)
{
	Count = Val;
	Values.resize(Count);
	for (int I = 0; I < Count; I++)
		Values[I] = Map.insert(make_pair("", I));
}

const string& discr_scheme::get_value(int I) const
{
	return Values[I]->first;
}

void discr_scheme::set_value(int I, const string& Val)
{
	Map.erase(Values[I]);
	Values[I] = Map.insert(make_pair(Val, I));
}

int discr_scheme::find(const string& Val) const
{
	pair<value_map::const_iterator, value_map::const_iterator> Range = Map.equal_range(Val);
	if (Range.first == Range.second) throw runtime_error("Discrete value doesn't exist");
	if (Range.first != --Range.second) throw runtime_error("Duplicate discrete values");
	return Range.first->second;
}

int discr_scheme::add(const string& Val)
{
	Values.push_back(Map.insert(make_pair(Val, Count++)));
	return Values.back()->second;
}

int discr_scheme::enter(const string& Val)
{
	pair<value_map::const_iterator, value_map::const_iterator> Range = Map.equal_range(Val);
	if (Range.first == Range.second) return add(Val);
	if (Range.first != --Range.second) throw runtime_error("Duplicate discrete values");
	return Range.first->second;
}

bool discr_scheme::exist(const string& Val) const
{
	return Map.find(Val) != Map.end();
}

void discr_scheme::sort()
{
	int Index = 0;
	for (value_map::iterator It = Map.begin(); It != Map.end(); ++It, Index++)
	{
		Values[Index] = It;
		It->second = Index;
	}
}

bool discr_scheme::valid(int Val) const
{
	return Val >= 0 && Val < Count;
}

discr_domain discr_scheme::to_domain() const
{
	return discr_domain(Count);
}

void discr_scheme::format(file_formatter& Fmt)
{
	if (Fmt.is_reading())
	{
		clear();
		string S;
		for (;;)
		{
			Fmt >> S;
			add(S);
			if (Fmt.peek_nonblank() != ',') break;
			Fmt << ',';
		}
	}
	else
		for (int I = 0; I < Count; I++)
		{
			if (I > 0) Fmt << ',';
			Fmt >> Values[I]->first;
		}
}

void discr_scheme::serialize(serializer& Serializer)
{
	if (Serializer.is_loading())
	{
		clear();
		Serializer("Count", Count);
		Serializer.begin_property("Values");
		for (int I = 0; I < Count; I++)
			Values.push_back(Map.insert(make_pair(Serializer[""].get<string>(), I)));
		Serializer.end_property();
	}
	else
	{
		Serializer("Count", Count);
		Serializer.begin_property("Values");
		for (int I = 0; I < Count; I++)
			Serializer[""].put(Values[I]->first);
		Serializer.end_property();
	}
}

attribute_scheme::attribute_scheme()
	: type(cont_attr)
{
}

void attribute_scheme::clear()
{
	name.clear();
	discr.clear();
}

bool attribute_scheme::valid(attribute_value Val) const
{
	if (type == cont_attr) return cont.valid(Val.cont);
	if (type == discr_attr) return discr.valid(Val.discr);
	return false;
}

attribute_domain attribute_scheme::to_domain() const
{
	if (type == cont_attr) return cont.to_domain();
	return discr.to_domain();
}

void attribute_scheme::format(file_formatter& Fmt)
{
	format_names(Fmt);
	Fmt << '(';
	if (type == cont_attr) Fmt >> cont;
	else Fmt >> discr;
	Fmt << ')';
}

void attribute_scheme::format_names(file_formatter& Fmt)
{
	if (Fmt.is_reading()) clear();
	Fmt >> name << ": ";
	if (Fmt.is_reading())
	{
		string S;
		Fmt >> S;
		type = str_to_attribute_type(S);
	}
	else
		Fmt >> attribute_type_to_str(type);
}

void attribute_scheme::serialize(serializer& Serializer)
{
	Serializer("Name", name);
	if (Serializer.is_loading())
	{
		discr.clear();
		type = str_to_attribute_type(Serializer["Type"].get<string>());
	}
	else
		Serializer["Type"].put(attribute_type_to_str(type));
	if (type == cont_attr) Serializer("Cont", cont);
	else Serializer("Discr", discr);
}

record_scheme::record_scheme(int SetSize, const attribute_scheme& SetValue)
	: array<attribute_scheme>(SetSize, SetValue)
{
}

bool record_scheme::valid(const record& Val) const
{
	if (Val.size() != size()) return false;
	for (int I = 0; I != size(); I++) if (!(*this)[I].valid(Val[I])) return false;
	return true;
}

record_domain record_scheme::to_domain() const
{
	record_domain Domain(size());
	for (int I = 0; I < size(); I++) Domain[I] = (*this)[I].to_domain();
	return Domain;
}

void record_scheme::load(const string& Filename)
{
	smart_file File(Filename, "r");
	file_formatter Fmt = get_formatter(File);
	format(Fmt);
	Fmt.finish();
}

void record_scheme::load_names(const string& Filename)
{
	smart_file File(Filename, "r");
	file_formatter Fmt = get_formatter(File);
	format_names(Fmt);
	Fmt.finish();
}

void record_scheme::save(const string& Filename) const
{
	smart_file File(Filename, "w");
	file_formatter Fmt = get_formatter(File);
	Fmt.set_writing();
	const_cast<record_scheme&>(*this).format(Fmt);
}

void record_scheme::save_names(const string& Filename) const
{
	smart_file File(Filename, "w");
	file_formatter Fmt = get_formatter(File);
	Fmt.set_writing();
	const_cast<record_scheme&>(*this).format_names(Fmt);
}

void record_scheme::format(file_formatter& Fmt)
{
	format(Fmt, false);
}
void record_scheme::format_names(file_formatter& Fmt)
{
	format(Fmt, true);
}

void record_scheme::serialize(serializer& Serializer)
{
	array<attribute_scheme>& V = *this;
	Serializer("Attributes", V);
}

file_formatter record_scheme::get_formatter(FILE* File)
{
	file_formatter Fmt(File);
	Fmt.set_blanks("\t ");
	Fmt.set_delimiters("\n,.:()");
	return Fmt;
}

void record_scheme::format(file_formatter& Fmt, bool Names)
{
	if (Fmt.is_reading())
	{
		clear();
		attribute_scheme Classes;
		Classes.name = "class";
		Classes.type = discr_attr;
		Fmt >> Classes.discr << ".\n";
		vector<attribute_scheme> Attrs;
		while (Fmt.is_alpha(Fmt.peek_nonblank()))
		{
			attribute_scheme Attr;
			if (Names) Attr.format_names(Fmt);
			else Attr.format(Fmt);
			Fmt << ".\n";
			Attrs.push_back(Attr);
		}
		Attrs.push_back(Classes);
		assign(begin_ptr(Attrs), end_ptr(Attrs));
	}
	else
	{
		assert(back().type == discr_attr);
		Fmt >> back().discr << ".\n";
		for (int I = 0; I < size() - 1; I++)
		{
			if (Names) ((*this)[I]).format_names(Fmt);
			else ((*this)[I]).format(Fmt);
			Fmt << ".\n";
		}
	}
}

record_scheme make_scheme(const string& Filename)
{
	record_scheme Scheme;
	Scheme.load(Filename);
	return Scheme;
}

}
